package com.example.camerademo;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CameraActivity extends AppCompatActivity {

    private final String TAG = "CameraActivity";
    private String mCameraId;
    private CameraCharacteristics mCameraCharacteristics;
    private Surface mSurfaceView;
    private CameraManager mCameraManager;
    private HandlerThread handlerThread;
    private Handler mBackgroundHandler;
    private ImageReader mImageReader;
    private CameraDevice cameraDevice;

    private boolean hasPermission() {
        return ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera2);

        TextureView mPreview = findViewById(R.id.preview);
        findViewById(R.id.take_photo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startTakePhoto(cameraDevice);
            }
        });
        initCamera();

        mPreview.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @RequiresApi(api = Build.VERSION_CODES.KITKAT)
            @Override
            public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
                Size mOptimalSize = getOptimalSize(width, height);
                mImageReader = ImageReader.newInstance(mOptimalSize.getWidth(), mOptimalSize.getHeight(), ImageFormat.JPEG, 1);
                Log.i(TAG, "===getOptimalSize===>" + mOptimalSize.getWidth() + "," + mOptimalSize.getHeight());
                if (mOptimalSize == null) {
                    Toast.makeText(CameraActivity.this, "获取预览尺寸失败", Toast.LENGTH_SHORT).show();
                } else {
                    surface.setDefaultBufferSize(mOptimalSize.getWidth(), mOptimalSize.getHeight());
                    mSurfaceView = new Surface(surface);
                    openCamera();
                }
            }

            @Override
            public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {

            }
        });
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void initCamera() {
        mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            String[] cameraIdList = mCameraManager.getCameraIdList();
            if (cameraIdList == null || cameraIdList.length == 0) {
                Toast.makeText(this, "当前没有可用摄像头", Toast.LENGTH_SHORT).show();
            } else {
                mCameraId = cameraIdList[CameraCharacteristics.LENS_FACING_FRONT];
                mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @SuppressLint("MissingPermission")
    private void openCamera() {
        if (mCameraManager == null || mCameraId == null) {
            Toast.makeText(this, "相机参数初始化失败！", Toast.LENGTH_SHORT).show();
        } else {
            startBackgroundThread();
            if (hasPermission()) {
                try {
                    mCameraManager.openCamera(mCameraId, new CameraDevice.StateCallback() {
                        @Override
                        public void onOpened(@NonNull CameraDevice camera) {
                            cameraDevice = camera;
                            try {
                                startPreview(camera);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        @Override
                        public void onDisconnected(@NonNull CameraDevice camera) {

                        }

                        @Override
                        public void onError(@NonNull CameraDevice camera, int error) {

                        }
                    }, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void startPreview(@NonNull CameraDevice camera) throws CameraAccessException {
        CaptureRequest.Builder captureRequest = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        captureRequest.addTarget(mSurfaceView);
        captureRequest.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自动开启闪关灯.
        captureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);//自动聚焦

        camera.createCaptureSession(Arrays.asList(mSurfaceView, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
            @Override
            public void onConfigured(@NonNull CameraCaptureSession session) {
                try {
                    session.setRepeatingRequest(captureRequest.build(), new CameraCaptureSession.CaptureCallback() {
                        @Override
                        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                            super.onCaptureCompleted(session, request, result);
                            Log.i(TAG,"===onCaptureCompleted===>");
                        }

                        @Override
                        public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                            super.onCaptureFailed(session, request, failure);
                            Log.i(TAG,"===onCaptureFailed===>");
                        }
                    }, mBackgroundHandler);
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onConfigureFailed(@NonNull CameraCaptureSession session) {

            }
        }, mBackgroundHandler);
    }

    private void startTakePhoto(CameraDevice device){
        try {
            CaptureRequest.Builder captureRequest = device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            captureRequest.addTarget(mSurfaceView);

            device.createCaptureSession(Arrays.asList(mSurfaceView, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    try {
                        session.capture(captureRequest.build(), new CameraCaptureSession.CaptureCallback() {
                            @Override
                            public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                                super.onCaptureCompleted(session, request, result);
                                Log.i(TAG,"===onCaptureCompleted===>拍照");
                            }

                            @Override
                            public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
                                super.onCaptureFailed(session, request, failure);
                                Log.i(TAG,"===onCaptureFailed===>");
                            }
                        }, mBackgroundHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                }
            }, mBackgroundHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private Size getOptimalSize(int maxWidth, int maxHeight) {
        if (mCameraCharacteristics == null) {
            return null;
        } else {
            float aspectRatio = (float) maxWidth / maxHeight;
            StreamConfigurationMap configurationMap = mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            Size[] outputSizes = configurationMap.getOutputSizes(SurfaceTexture.class);
            //优先选取宽高比例相同的预览尺寸
            List<Size> ratioEquals = new ArrayList<>();
            for (Size size : outputSizes) {
                Log.i(TAG, "===outputSizes===>" + size.getWidth() + "," + size.getHeight());
                if ((float) size.getWidth() / size.getHeight() == aspectRatio) {
                    ratioEquals.add(size);
                }
            }

            Size optimalSize = null;
            //再筛选出与控件尺寸最接近的
            if (ratioEquals.size() != 0) {
                int minGap = Integer.MAX_VALUE;
                for (int i = 0; i < ratioEquals.size(); i++) {
                    Size size = ratioEquals.get(i);
                    int curGap = Math.abs(size.getWidth() - maxWidth);
                    if (curGap < minGap) {
                        minGap = curGap;
                        optimalSize = size;
                    }
                }
                return optimalSize;
            }

            //如果没有宽高比例相同的预览尺寸，则退而求其次选择宽高比例最接近的
            float minGap = Float.MAX_VALUE;
            for (int i = 0; i < outputSizes.length; i++) {
                Size size = outputSizes[i];
                float curGap = Math.abs((float) size.getWidth() / size.getHeight() - aspectRatio);
                if (curGap < minGap) {
                    minGap = curGap;
                    optimalSize = size;
                }
            }
            return optimalSize;
        }
    }

    private void startBackgroundThread() {
        handlerThread = new HandlerThread("CameraThread");
        handlerThread.start();
        mBackgroundHandler = new Handler(handlerThread.getLooper());
    }

    private void stopBackgroundThread() {
        if (handlerThread != null) {
            handlerThread.quitSafely();
            try {
                handlerThread.join();
                handlerThread = null;
                mBackgroundHandler = null;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopBackgroundThread();
    }
}
